package org.liveboardgames.agricola.business;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.liveboardgames.agricola.access.ICardDao;
import org.liveboardgames.agricola.access.IGameDao;
import org.liveboardgames.agricola.business.helper.GameHelper;
import org.liveboardgames.agricola.business.updater.FarmTileUpdater;
import org.liveboardgames.agricola.business.updater.FarmUpdater;
import org.liveboardgames.agricola.business.updater.GameUpdater;
import org.liveboardgames.agricola.controller.AgricolaUpdateAdapter;
import org.liveboardgames.agricola.domain.Change;
import org.liveboardgames.agricola.domain.Game;
import org.liveboardgames.agricola.domain.card.Card;
import org.liveboardgames.agricola.domain.farm.FarmTile;
import org.liveboardgames.agricola.domain.player.Player;
import org.liveboardgames.agricola.model.enums.FarmElementEnum;
import org.liveboardgames.agricola.model.enums.ResourceTypeEnum;

public class HarvestServiceImpl implements IHarvestService {

  private IGameDao gameDao;

  private IRoundService roundService;

  private ICardDao cardDao;

  public void setCardDao(ICardDao cardDao) {
    this.cardDao = cardDao;
  }

  public void setGameDao(IGameDao gameDao) {
    this.gameDao = gameDao;
  }

  public void setRoundService(IRoundService roundService) {
    this.roundService = roundService;
  }

  public void resetHarvest(Long gameId) {
    Game game = gameDao.load(gameId);

    // Reset the harvest variables
    List<Change> changes = new ArrayList<Change>();
    new GameUpdater(game, changes).resetHarvest();
    
    new AgricolaUpdateAdapter().publishGameUpdates(gameId, changes);

    // Start a new round
    roundService.startRound(game.getId());
  }

  public void harvestField(Long gameId, Long playerId) {
    Game game = gameDao.load(gameId);
    GameHelper gameHelper = new GameHelper();
    Player player = gameHelper.findPlayer(game, playerId);

    List<Change> changes = new ArrayList<Change>();

    List<FarmTile> tiles = player.getFarm().getTiles();

    for (FarmTile tile : tiles) {
      String farmElement = tile.getFarmElement();
      if (FarmElementEnum.FIELD.equalsIgnoreCase(farmElement)) {
        Map<String, Integer> resourcesOnTile = tile.getResources();
        if (resourcesOnTile != null && !resourcesOnTile.isEmpty()) {
          Set<String> resourceTypes = resourcesOnTile.keySet();
          if (resourceTypes != null && !resourceTypes.isEmpty()) {
            for (String resourceType : resourceTypes) {
              // Take one resource from the field
              new FarmTileUpdater(tile, changes).addResource(resourceType, -1);

              // And add it to the stock
              new FarmUpdater(game, player.getFarm(), changes).addResource(resourceType, 1);
            }
          }
        }
      }
    }

    // Signal the player has harvested.
    GameUpdater gameUpdater = new GameUpdater(game, changes);
    gameUpdater.addHarvestFieldPlayer(player);

    // Update the current player, which will be the next one to harvest
    String harvestStep = gameHelper.findNextHarvestStep(game);
    Player nextPlayerToHarvest = gameHelper.getHarvestingPlayer(game, harvestStep);
    gameUpdater.setCurrentPlayer(nextPlayerToHarvest);

    gameDao.save(game);

    new AgricolaUpdateAdapter().publishGameUpdates(gameId, changes);
  }

  public void harvestFeed(Long gameId, Long playerId, Integer foodNeeded, Integer foodGiven) {
    Game game = gameDao.load(gameId);
    GameHelper gameHelper = new GameHelper();
    Player player = gameHelper.findPlayer(game, playerId);

    List<Change> changes = new ArrayList<Change>();
    FarmUpdater farmUpdater = new FarmUpdater(game, player.getFarm(), changes);

    // Add applicable begging cards
    int foodMissing = foodNeeded - foodGiven;
    if (foodMissing > 0) {
      Set<Card> beggingCards = cardDao.getBeggingCards(foodMissing);
      for (Iterator<Card> iterator = beggingCards.iterator(); iterator.hasNext();) {
        Card beggingCard = iterator.next();
        farmUpdater.addPlayedCard(beggingCard);
      }
    }

    // If too much food was given, give the excess back to the player
    int excessFood = -foodMissing;
    if (excessFood > 0) {
      farmUpdater.addResource(ResourceTypeEnum.FOOD, excessFood);
    }

    // Signal the player has harvested.
    GameUpdater gameUpdater = new GameUpdater(game, changes);
    gameUpdater.addHarvestFeedPlayer(player);

    // Update the current player, which will be the next one to harvest
    String harvestStep = gameHelper.findNextHarvestStep(game);
    Player nextPlayerToHarvest = gameHelper.getHarvestingPlayer(game, harvestStep);
    gameUpdater.setCurrentPlayer(nextPlayerToHarvest);

    gameDao.save(game);

    new AgricolaUpdateAdapter().publishGameUpdates(gameId, changes);
  }

  public void harvestStepComplete(Long gameId, Long playerId) {
    Game game = gameDao.load(gameId);
    GameHelper gameHelper = new GameHelper();
    Player player = gameHelper.findPlayer(game, playerId);

    List<Change> changes = new ArrayList<Change>();

    // Signal the player has harvested.
    GameUpdater gameUpdater = new GameUpdater(game, changes);
    gameUpdater.addHarvestBreedPlayer(player);

    // Update the current player, which will be the next one to harvest
    String harvestStep = gameHelper.findNextHarvestStep(game);
    if (harvestStep != null) {
      Player nextPlayerToHarvest = gameHelper.getHarvestingPlayer(game, harvestStep);
      gameUpdater.setCurrentPlayer(nextPlayerToHarvest);
      new AgricolaUpdateAdapter().publishGameUpdates(gameId, changes);
      gameDao.save(game);
    }
    else {
      new AgricolaUpdateAdapter().publishGameUpdates(gameId, changes);
      gameDao.save(game);
      resetHarvest(gameId);
    }
  }
}
